﻿namespace ImageP
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    
    /// <summary>
    /// class for all enhancing operations on an image
    /// </summary>
    public class Enhancements
    {
        /// <summary>
        /// indicates the rounded red values
        /// </summary>
        public int[] RoundedRed = new int[256];

        /// <summary>
        /// indicates the rounded green values
        /// </summary>
        public int[] RoundedGreen = new int[256];

        /// <summary>
        /// indicates the rounded blue values
        /// </summary>
        public int[] RoundedBlue = new int[256];

        /// <summary>
        /// buffer of type RGBcolor
        /// </summary>
        public RGBcolor[,] EnhancedBuffer;

        /// <summary>
        /// indicating image width
        /// </summary>
        private int width;

        /// <summary>
        /// indicating image height
        /// </summary>
        private int height;

        /// <summary>
        /// indicating red values within an image
        /// </summary>
        private int[] valuesRed;

        /// <summary>
        /// indicating green values within an image
        /// </summary>
        private int[] valuesGreen;

        /// <summary>
        /// indicating blue values within an image
        /// </summary>
        private int[] valuesBlue;
        
        /// <summary>
        /// function to equalize coming values in the buffer.
        /// </summary>
        /// <param name="buffer">buffer</param>
        /// <param name="w">w</param>
        /// <param name="h">h</param>
        /// <returns>RGBcolor</returns>
        public RGBcolor[,] Equalize(RGBcolor[,] buffer, int w, int h)
        {
            this.EnhancedBuffer = new RGBcolor[h, w];
            this.width = w;
            this.height = h;
            int countedSum_value = 0;
            double calculation = 0;
            int new_value = 0;
            
            double total_Rvalue = 0;
            double total_Gvalue = 0;
            double total_Bvalue = 0;

            double max_Rvalue = 0;
            double max_Gvalue = 0;
            double max_Bvalue = 0;
            int buffer_value = 0;
            this.valuesRed = new int[256];
            this.valuesGreen = new int[256];
            this.valuesBlue = new int[256];
            this.valuesRed.Initialize();
            this.valuesGreen.Initialize();
            this.valuesBlue.Initialize();
            for (int i = 0; i < this.height; i++)
            {
                for (int j = 0; j < this.width; j++)
                {
                    buffer_value = buffer[i, j].Red;
                    this.valuesRed[buffer_value]++;
                    if (buffer_value > max_Rvalue)
                    {
                        max_Rvalue = buffer_value;
                    }

                    buffer_value = buffer[i, j].Green;
                    this.valuesGreen[buffer_value]++;
                    if (buffer_value > max_Gvalue)
                    {
                        max_Gvalue = buffer_value;
                    }

                    buffer_value = buffer[i, j].Blue;
                    this.valuesBlue[buffer_value]++;
                    if (buffer_value > max_Bvalue)
                    {
                        max_Bvalue = buffer_value;
                    }

                    total_Rvalue++;
                    total_Bvalue++;
                    total_Gvalue++;
                }
            }

            for (int i = 0; i < 255; i++)
            {
                this.valuesRed[i + 1] += this.valuesRed[i];
                this.valuesGreen[i + 1] += this.valuesGreen[i];
                this.valuesBlue[i + 1] += this.valuesBlue[i];
            }

            for (int i = 0; i < this.height; i++)
            {
                for (int j = 0; j < this.width; j++)
                {
                    countedSum_value = this.valuesRed[buffer[i, j].Red];
                    calculation = (countedSum_value / total_Rvalue) * max_Rvalue;
                    new_value = (int)Math.Round(calculation);
                    this.RoundedRed[buffer[i, j].Red] = new_value;
                    this.EnhancedBuffer[i, j].Red = new_value;
                    calculation = 0;

                    countedSum_value = this.valuesGreen[buffer[i, j].Green];
                    calculation = (countedSum_value / total_Gvalue) * max_Gvalue;
                    new_value = (int)Math.Round(calculation);
                    this.RoundedGreen[buffer[i, j].Green] = new_value;
                    this.EnhancedBuffer[i, j].Green = new_value;
                    calculation = 0;
                    countedSum_value = this.valuesBlue[buffer[i, j].Blue];
                    calculation = (countedSum_value / total_Bvalue) * max_Bvalue;
                    new_value = (int)Math.Round(calculation);
                    this.RoundedBlue[buffer[i, j].Blue] = new_value;
                    this.EnhancedBuffer[i, j].Blue = new_value;
                    calculation = 0;
                }
            }

            return this.EnhancedBuffer;
        }

        /// <summary>
        /// gets rounded values for the color red
        /// </summary>
        /// <returns>int</returns>
        public int[] GetRoundedRed()
        {
            return this.RoundedRed;
        }

        /// <summary>
        /// gets rounded values for the color green
        /// </summary>
        /// <returns>int</returns>
        public int[] GetRoundedGreen()
        {
            return this.RoundedGreen;
        }

        /// <summary>
        /// gets rounded values for the color blue
        /// </summary>
        /// <returns>int</returns>
        public int[] GetRoundedBlue()
        {
            return this.RoundedBlue;
        }
      
        /// <summary>
        /// sets the brightness of a selected image
        /// </summary>
        /// <param name="offset">offset</param>
        /// <param name="image">image</param>
        /// <returns>bitmap</returns>
        public Bitmap SetBrightness(int offset, Image image)
        {
            Bitmap bm = new Bitmap(Convert.ToInt32(image.Width), Convert.ToInt32(image.Height));
            Bitmap old = new Bitmap(image);
            Color cl;
            int newValR, newValG, newValB;
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    cl = old.GetPixel(j, i);

                    newValR = offset + cl.R;
                    newValG = offset + cl.G;
                    newValB = offset + cl.B;

                    if (newValR >= 255)
                    {
                        newValR = 255;
                    }
                    else if (newValR <= 0)
                    {
                        newValR = 0;
                    }

                    if (newValG >= 255)
                    {
                        newValG = 255;
                    }
                    else if (newValG <= 0)
                    {
                        newValG = 0;
                    }

                    if (newValB >= 255)
                    {
                        newValB = 255;
                    }
                    else if (newValB <= 0)
                    {
                        newValB = 0;
                    }

                    Color clr = Color.FromArgb(255, newValR, newValG, newValB);
                    bm.SetPixel(j, i, clr);
                }
            }

            return bm;
        }

        /// <summary>
        /// sets the contrast of a given image.
        /// </summary>
        /// <param name="newMin">newMin</param>
        /// <param name="newMax">newMax</param>
        /// <param name="image">image</param>
        /// <returns>bitmap</returns>
        public Bitmap SetContrast(int newMin, int newMax, Image image)
        {
            Bitmap old = new Bitmap(image);
            Bitmap bm = new Bitmap(image.Width, image.Height);
            int oldMinR = 255;
            int oldMaxR = 0;
            int oldMinG = 255;
            int oldMaxG = 0;
            int oldMinB = 255;
            int oldMaxB = 0;
            int newValR, newValG, newValB;
            int clrR, clrG, clrB;

            if (newMax > 255)
            {
                newMax = 255;
            }
            else if (newMax < 0)
            {
                newMax = 0;
            }

            if (newMin > 255)
            {
                newMin = 255;
            }
            else if (newMin < 0)
            {
                newMin = 0;
            }

            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    ////Red
                    clrR = old.GetPixel(j, i).R;
                    if (Convert.ToInt32(clrR) < Convert.ToInt32(oldMinR))
                    {
                        oldMinR = clrR;
                    }

                    if (Convert.ToInt32(clrR) > Convert.ToInt32(oldMaxR))
                    {
                        oldMaxR = clrR;
                    }

                    ////Green
                    clrG = old.GetPixel(j, i).G;
                    if (Convert.ToInt32(clrG) < Convert.ToInt32(oldMinG))
                    {
                        oldMinG = clrG;
                    }

                    if (Convert.ToInt32(clrG) > Convert.ToInt32(oldMaxG))
                    {
                        oldMaxG = clrG;
                    }

                    ////blue
                    clrB = old.GetPixel(j, i).B;
                    if (Convert.ToInt32(clrB) < Convert.ToInt32(oldMinB))
                    {
                        oldMinB = clrB;
                    }

                    if (Convert.ToInt32(clrB) > Convert.ToInt32(oldMaxB))
                    {
                        oldMaxB = clrB;
                    }
                }
            }

            Color clr;
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    clr = old.GetPixel(j, i);
                    newValR = (int)(((Convert.ToDouble(clr.R) - Convert.ToDouble(oldMinR)) / (Convert.ToDouble(oldMaxR) - Convert.ToDouble(oldMinR))) * ((double)(newMax - newMin) + newMin));
                    newValG = (int)(((Convert.ToDouble(clr.G) - Convert.ToDouble(oldMinG)) / (Convert.ToDouble(oldMaxG) - Convert.ToDouble(oldMinG))) * ((double)(newMax - newMin) + newMin));
                    newValB = (int)(((Convert.ToDouble(clr.B) - Convert.ToDouble(oldMinB)) / (Convert.ToDouble(oldMaxB) - Convert.ToDouble(oldMinB))) * ((double)(newMax - newMin) + newMin));

                    if (newValR > 255)
                    {
                        newValR = 255;
                    }
                    else if (newValR < 0)
                    {
                        newValR = 0;
                    }

                    if (newValG > 255)
                    {
                        newValG = 255;
                    }
                    else if (newValG < 0)
                    {
                        newValG = 0;
                    }

                    if (newValB > 255)
                    {
                        newValB = 255;
                    }
                    else if (newValB < 0)
                    {
                        newValG = 0;
                    }

                    Color cl = Color.FromArgb(255, newValR, newValG, newValB);
                    bm.SetPixel(j, i, cl);
                }
            }

            return bm;
        }

        /// <summary>
        /// gets the gamma of the image
        /// </summary>
        /// <param name="gammaValue">gammaValue</param>
        /// <param name="image">image</param>
        /// <returns>bitmap</returns>
        public Bitmap Gamma(int gammaValue, Image image)
        {
            Bitmap old = new Bitmap(image);
            Bitmap final = new Bitmap(Convert.ToInt32(image.Width), Convert.ToInt32(image.Height));
            RGBcolor[,] arr = new RGBcolor[Convert.ToInt32(image.Height), Convert.ToInt32(image.Width)];
            int oldMinR = 255;
            int oldMaxR = 0;
            int oldMinG = 255;
            int oldMaxG = 0;
            int oldMinB = 255;
            int oldMaxB = 0;
            int newValR, newValB, newValG;

            Color cl;
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    cl = old.GetPixel(j, i);
                    arr[i, j].Red = (int)Math.Pow(Convert.ToDouble(cl.R), 1 / Convert.ToDouble(gammaValue));
                    arr[i, j].Green = (int)Math.Pow(Convert.ToDouble(cl.G), 1 / Convert.ToDouble(gammaValue));
                    arr[i, j].Blue = (int)Math.Pow(Convert.ToDouble(cl.B), 1 / Convert.ToDouble(gammaValue));

                    if (Convert.ToInt32(arr[i, j].Red) < Convert.ToInt32(oldMinR))
                    {
                        oldMinR = arr[i, j].Red;
                    }

                    if (Convert.ToInt32(arr[i, j].Red) > Convert.ToInt32(oldMaxR))
                    {
                        oldMaxR = arr[i, j].Red;
                    }

                    if (Convert.ToInt32(arr[i, j].Green) < Convert.ToInt32(oldMinG))
                    {
                        oldMinG = arr[i, j].Green;
                    }

                    if (Convert.ToInt32(arr[i, j].Green) > Convert.ToInt32(oldMaxG))
                    {
                        oldMaxG = arr[i, j].Green;
                    }

                    if (Convert.ToInt32(arr[i, j].Blue) < Convert.ToInt32(oldMinB))
                    {
                        oldMinB = arr[i, j].Blue;
                    }

                    if (Convert.ToInt32(arr[i, j].Blue) > Convert.ToInt32(oldMaxB))
                    {
                        oldMaxB = arr[i, j].Blue;
                    }
                }
            }

            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    newValR = (int)((((double)arr[i, j].Red - (double)oldMinR) / ((double)oldMaxR - (double)oldMinR)) * 255);
                    newValG = (int)((((double)arr[i, j].Green - (double)oldMinG) / ((double)oldMaxG - (double)oldMinG)) * 255);
                    newValB = (int)((((double)arr[i, j].Blue - (double)oldMinB) / ((double)oldMaxB - (double)oldMinB)) * 255);

                    Color clr = Color.FromArgb(255, newValR, newValG, newValB);
                    final.SetPixel(j, i, clr);
                }
            }

            return final;
        }

        /// <summary>
        /// turns the image to its greyscale.
        /// </summary>
        /// <param name="image">image</param>
        /// <returns>bitmap</returns>
        public Bitmap GreyScale(Image image)
        {
            int grey;
            Bitmap bm = new Bitmap(Convert.ToInt32(image.Width), Convert.ToInt32(image.Height));
            Bitmap old = new Bitmap(image);
            Color cl;
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    cl = old.GetPixel(j, i);
                    grey = (cl.R + cl.G + cl.B) / 3;
                    Color clr = Color.FromArgb(grey, grey, grey);
                    bm.SetPixel(j, i, clr);
                }

            }
            return bm;
        }
    }
}
